package com.reactim.message.protection;

import com.reactim.message.config.ConnectionConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 资源保护服务
 * 负责连接数量限制、请求频率控制和资源使用监控
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ResourceProtectionService {

    private final ConnectionConfig connectionConfig;

    // 全局连接计数
    private final AtomicInteger globalConnectionCount = new AtomicInteger(0);
    
    // 用户连接计数
    private final Map<Long, AtomicInteger> userConnectionCounts = new ConcurrentHashMap<>();
    
    // IP连接计数
    private final Map<String, AtomicInteger> ipConnectionCounts = new ConcurrentHashMap<>();
    
    // 请求频率限制 - 用户级别
    private final Map<Long, RateLimiter> userRateLimiters = new ConcurrentHashMap<>();
    
    // 请求频率限制 - IP级别
    private final Map<String, RateLimiter> ipRateLimiters = new ConcurrentHashMap<>();
    
    // 异常连接检测
    private final Map<Long, ConnectionBehavior> connectionBehaviors = new ConcurrentHashMap<>();
    
    // 资源使用统计
    private final AtomicLong totalConnectionsCreated = new AtomicLong(0);
    private final AtomicLong totalConnectionsRejected = new AtomicLong(0);
    private final AtomicLong totalRequestsRateLimited = new AtomicLong(0);

    /**
     * 检查是否允许新连接
     *
     * @param userId 用户ID
     * @param clientIp 客户端IP
     * @return 是否允许连接
     */
    public Mono<ConnectionCheckResult> checkConnectionAllowed(Long userId, String clientIp) {
        try {
            // 1. 检查全局连接数限制
            if (globalConnectionCount.get() >= getMaxGlobalConnections()) {
                totalConnectionsRejected.incrementAndGet();
                log.warn("全局连接数已达上限: current={}, max={}", 
                    globalConnectionCount.get(), getMaxGlobalConnections());
                return Mono.just(ConnectionCheckResult.rejected("全局连接数已达上限"));
            }

            // 2. 检查用户连接数限制
            int userConnections = userConnectionCounts.computeIfAbsent(userId, k -> new AtomicInteger(0)).get();
            if (userConnections >= getMaxUserConnections()) {
                totalConnectionsRejected.incrementAndGet();
                log.warn("用户连接数已达上限: userId={}, current={}, max={}", 
                    userId, userConnections, getMaxUserConnections());
                return Mono.just(ConnectionCheckResult.rejected("用户连接数已达上限"));
            }

            // 3. 检查IP连接数限制
            int ipConnections = ipConnectionCounts.computeIfAbsent(clientIp, k -> new AtomicInteger(0)).get();
            if (ipConnections >= getMaxIpConnections()) {
                totalConnectionsRejected.incrementAndGet();
                log.warn("IP连接数已达上限: ip={}, current={}, max={}", 
                    clientIp, ipConnections, getMaxIpConnections());
                return Mono.just(ConnectionCheckResult.rejected("IP连接数已达上限"));
            }

            // 4. 检查用户请求频率
            RateLimiter userRateLimiter = userRateLimiters.computeIfAbsent(userId, 
                k -> new RateLimiter(getUserRateLimit(), Duration.ofMinutes(1)));
            
            if (!userRateLimiter.tryAcquire()) {
                totalRequestsRateLimited.incrementAndGet();
                log.warn("用户请求频率超限: userId={}", userId);
                return Mono.just(ConnectionCheckResult.rejected("用户请求频率超限"));
            }

            // 5. 检查IP请求频率
            RateLimiter ipRateLimiter = ipRateLimiters.computeIfAbsent(clientIp, 
                k -> new RateLimiter(getIpRateLimit(), Duration.ofMinutes(1)));
            
            if (!ipRateLimiter.tryAcquire()) {
                totalRequestsRateLimited.incrementAndGet();
                log.warn("IP请求频率超限: ip={}", clientIp);
                return Mono.just(ConnectionCheckResult.rejected("IP请求频率超限"));
            }

            log.debug("连接检查通过: userId={}, ip={}", userId, clientIp);
            return Mono.just(ConnectionCheckResult.allowed());

        } catch (Exception e) {
            log.error("连接检查失败: userId={}, ip={}, error={}", userId, clientIp, e.getMessage());
            return Mono.just(ConnectionCheckResult.rejected("连接检查失败: " + e.getMessage()));
        }
    }

    /**
     * 记录连接建立
     *
     * @param userId 用户ID
     * @param clientIp 客户端IP
     */
    public void recordConnectionEstablished(Long userId, String clientIp) {
        try {
            // 增加计数
            globalConnectionCount.incrementAndGet();
            userConnectionCounts.computeIfAbsent(userId, k -> new AtomicInteger(0)).incrementAndGet();
            ipConnectionCounts.computeIfAbsent(clientIp, k -> new AtomicInteger(0)).incrementAndGet();
            
            // 记录连接行为
            connectionBehaviors.put(userId, new ConnectionBehavior(userId, clientIp, Instant.now()));
            
            // 统计
            totalConnectionsCreated.incrementAndGet();
            
            log.debug("连接已建立: userId={}, ip={}, globalCount={}", 
                userId, clientIp, globalConnectionCount.get());

        } catch (Exception e) {
            log.error("记录连接建立失败: userId={}, ip={}, error={}", userId, clientIp, e.getMessage());
        }
    }

    /**
     * 记录连接断开
     *
     * @param userId 用户ID
     * @param clientIp 客户端IP
     */
    public void recordConnectionClosed(Long userId, String clientIp) {
        try {
            // 减少计数
            globalConnectionCount.decrementAndGet();
            
            AtomicInteger userCount = userConnectionCounts.get(userId);
            if (userCount != null) {
                userCount.decrementAndGet();
                if (userCount.get() <= 0) {
                    userConnectionCounts.remove(userId);
                }
            }
            
            AtomicInteger ipCount = ipConnectionCounts.get(clientIp);
            if (ipCount != null) {
                ipCount.decrementAndGet();
                if (ipCount.get() <= 0) {
                    ipConnectionCounts.remove(clientIp);
                }
            }
            
            // 移除连接行为记录
            connectionBehaviors.remove(userId);
            
            log.debug("连接已断开: userId={}, ip={}, globalCount={}", 
                userId, clientIp, globalConnectionCount.get());

        } catch (Exception e) {
            log.error("记录连接断开失败: userId={}, ip={}, error={}", userId, clientIp, e.getMessage());
        }
    }

    /**
     * 检查请求频率限制
     *
     * @param userId 用户ID
     * @param clientIp 客户端IP
     * @return 是否允许请求
     */
    public Mono<Boolean> checkRequestRateLimit(Long userId, String clientIp) {
        try {
            // 检查用户级别限流
            RateLimiter userRateLimiter = userRateLimiters.get(userId);
            if (userRateLimiter != null && !userRateLimiter.tryAcquire()) {
                totalRequestsRateLimited.incrementAndGet();
                log.debug("用户请求被限流: userId={}", userId);
                return Mono.just(false);
            }

            // 检查IP级别限流
            RateLimiter ipRateLimiter = ipRateLimiters.get(clientIp);
            if (ipRateLimiter != null && !ipRateLimiter.tryAcquire()) {
                totalRequestsRateLimited.incrementAndGet();
                log.debug("IP请求被限流: ip={}", clientIp);
                return Mono.just(false);
            }

            return Mono.just(true);

        } catch (Exception e) {
            log.error("检查请求频率限制失败: userId={}, ip={}, error={}", userId, clientIp, e.getMessage());
            return Mono.just(false);
        }
    }

    /**
     * 检测异常连接行为
     *
     * @param userId 用户ID
     * @return 异常检测结果
     */
    public Mono<AnomalyDetectionResult> detectAnomalousConnection(Long userId) {
        try {
            ConnectionBehavior behavior = connectionBehaviors.get(userId);
            if (behavior == null) {
                return Mono.just(AnomalyDetectionResult.normal());
            }

            // 检查连接时长异常
            Duration connectionDuration = Duration.between(behavior.getConnectedAt(), Instant.now());
            if (connectionDuration.toHours() > getMaxConnectionHours()) {
                log.warn("检测到长时间连接异常: userId={}, duration={}小时", 
                    userId, connectionDuration.toHours());
                return Mono.just(AnomalyDetectionResult.anomalous("连接时间过长", 
                    "连接时长: " + connectionDuration.toHours() + "小时"));
            }

            // 检查请求频率异常
            RateLimiter userRateLimiter = userRateLimiters.get(userId);
            if (userRateLimiter != null && userRateLimiter.isExcessive()) {
                log.warn("检测到请求频率异常: userId={}", userId);
                return Mono.just(AnomalyDetectionResult.anomalous("请求频率异常", 
                    "请求频率超过正常范围"));
            }

            return Mono.just(AnomalyDetectionResult.normal());

        } catch (Exception e) {
            log.error("异常连接检测失败: userId={}, error={}", userId, e.getMessage());
            return Mono.just(AnomalyDetectionResult.error("检测失败: " + e.getMessage()));
        }
    }

    /**
     * 获取资源使用统计
     *
     * @return 资源使用统计
     */
    public ResourceUsageStats getResourceUsageStats() {
        return ResourceUsageStats.builder()
            .globalConnectionCount(globalConnectionCount.get())
            .userConnectionCounts(Map.copyOf(userConnectionCounts.entrySet().stream()
                .collect(java.util.stream.Collectors.toMap(
                    Map.Entry::getKey, 
                    entry -> entry.getValue().get()))))
            .ipConnectionCounts(Map.copyOf(ipConnectionCounts.entrySet().stream()
                .collect(java.util.stream.Collectors.toMap(
                    Map.Entry::getKey, 
                    entry -> entry.getValue().get()))))
            .totalConnectionsCreated(totalConnectionsCreated.get())
            .totalConnectionsRejected(totalConnectionsRejected.get())
            .totalRequestsRateLimited(totalRequestsRateLimited.get())
            .activeRateLimiters(userRateLimiters.size() + ipRateLimiters.size())
            .build();
    }

    /**
     * 清理过期的限流器和行为记录
     */
    public void cleanupExpiredData() {
        try {
            Instant now = Instant.now();
            
            // 清理过期的用户限流器
            userRateLimiters.entrySet().removeIf(entry -> entry.getValue().isExpired(now));
            
            // 清理过期的IP限流器
            ipRateLimiters.entrySet().removeIf(entry -> entry.getValue().isExpired(now));
            
            // 清理长时间未活动的连接行为记录
            connectionBehaviors.entrySet().removeIf(entry -> {
                Duration inactiveDuration = Duration.between(entry.getValue().getConnectedAt(), now);
                return inactiveDuration.toHours() > getMaxConnectionHours();
            });
            
            log.debug("清理过期数据完成: userRateLimiters={}, ipRateLimiters={}, connectionBehaviors={}", 
                userRateLimiters.size(), ipRateLimiters.size(), connectionBehaviors.size());

        } catch (Exception e) {
            log.error("清理过期数据失败: error={}", e.getMessage());
        }
    }

    // 配置获取方法（从配置文件获取，使用默认值）
    private int getMaxGlobalConnections() {
        return 10000; // 默认全局最大连接数
    }

    private int getMaxUserConnections() {
        return connectionConfig.getSecurity().getMaxConnectionsPerUser();
    }

    private int getMaxIpConnections() {
        return connectionConfig.getSecurity().getMaxConnectionsPerIp();
    }

    private int getUserRateLimit() {
        return 60; // 默认用户每分钟请求限制
    }

    private int getIpRateLimit() {
        return 600; // 默认IP每分钟请求限制
    }

    private int getMaxConnectionHours() {
        return 24; // 默认最大连接时长（小时）
    }
}